package utils

import (
	"bytes"
	"encoding/binary"
	"log"
	"net"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var icmp ICMP

type ICMP struct {
   Type        uint8
   Code        uint8
   Checksum    uint16
   Identifier  uint16
   SequenceNum uint16
}

// 删除多余的空格
func DeleteExtraSpace(s string) string {
	//删除字符串中的多余空格，有多个空格时，仅保留一个空格
	s1 := strings.Replace(s, "  ", " ", -1)      //替换tab为空格
	regstr := "\\s{2,}"                          //两个及两个以上空格的正则表达式
	reg, _ := regexp.Compile(regstr)             //编译正则表达式
	s2 := make([]byte, len(s1))                  //定义字符数组切片
	copy(s2, s1)                                 //将字符串复制到切片
	spc_index := reg.FindStringIndex(string(s2)) //在字符串中搜索
	for len(spc_index) > 0 {                     //找到适配项
		s2 = append(s2[:spc_index[0]+1], s2[spc_index[1]:]...) //删除多余空格
		spc_index = reg.FindStringIndex(string(s2))            //继续在字符串中搜索
	}
	return string(s2)
}

// 判断IPv4地址
func IsIpv4(ip string) bool {
	// 去掉端口
	if strings.Contains(ip, ":") {
		addr := strings.Split(ip, ":")
		port, err := strconv.Atoi(addr[1])
		if err != nil {
			log.Printf("%v无法转换成数字", port)
			return false
		}
		// 判断端口最大长度
		if port < 65535 {
			ip = addr[0]
		}
	}
	matched, err := regexp.MatchString("((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}", ip)
	if err != nil {
		return false
	}
	return matched
}


func Isping(ip string) bool {
	//开始填充数据包
	icmp.Type = 8 //8->echo message  0->reply message
	icmp.Code = 0
	icmp.Checksum = 0
	icmp.Identifier = 0
	icmp.SequenceNum = 0
 
	recvBuf := make([]byte, 32)
	var buffer bytes.Buffer
 
	//先在buffer中写入icmp数据报求去校验和
	binary.Write(&buffer, binary.BigEndian, icmp)
	icmp.Checksum = CheckSum(buffer.Bytes())
	//然后清空buffer并把求完校验和的icmp数据报写入其中准备发送
	buffer.Reset()
	binary.Write(&buffer, binary.BigEndian, icmp)
 
	Time, _ := time.ParseDuration("2s")
	conn, err := net.DialTimeout("ip4:icmp", ip, Time)
	if err != nil {
	   return false
	}
	_, err = conn.Write(buffer.Bytes())
	if err != nil {
	   log.Println("conn.Write error:", err)
	   return false
	}
	conn.SetReadDeadline(time.Now().Add(time.Second * 2))
	num, err := conn.Read(recvBuf)
	if err != nil {
	   log.Println("conn.Read error:", err)
	   return false
	}
 
	conn.SetReadDeadline(time.Time{})
 
	if string(recvBuf[0:num]) != "" {
	   return true
	}
	return false
 
 }
 
 func CheckSum(data []byte) uint16 {
	var (
	   sum    uint32
	   length int = len(data)
	   index  int
	)
	for length > 1 {
	   sum += uint32(data[index])<<8 + uint32(data[index+1])
	   index += 2
	   length -= 2
	}
	if length > 0 {
	   sum += uint32(data[index])
	}
	sum += (sum >> 16)
 
	return uint16(^sum)
 }
